﻿// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. 
// See License.txt in the project root for license information.
/*
 * Embedded List class
 * 
 * This is a class to supports lists used internally by Actors.
 * It makes use of the same list logic as a List Actor
 * and its state is stored in the ActorState.
 * 
 * It implements an IList style list.
 * 
 * The EmbeddedList can be used as a wrapper to an actor list.
 * The list is not destroyed when the class is released.
 * However Dispose may be called to destroy the list.
 * 
 */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using System.Collections.Specialized;
using System.Threading.Tasks;
using System.Threading.Actors;
using System.Collections;

namespace System.Threading.Actors.Helpers
{
    public class EmbeddedList : IEnumerable, IList, ICollection, IDisposable
    {
        private readonly IActorState m_state;
        private readonly string m_name;
        private bool m_disposed;

        // Constructor needs IActorState, list name, flag to allow Publish calls
        public EmbeddedList(IActorState state, string name, bool allowPub)
        {
            Contract.Requires(state != null);

            m_state = state;
            m_name = name;
            m_disposed = false;

            bool created = (bool)ListHelperMethods.TryConstruct(m_state, new object[] { m_name });

            if (!allowPub)
                ListHelperMethods.SetAllowPublish(m_state, new object[] { m_name, 0 });
        }

        // default to no publish
        public EmbeddedList(IActorState state, string name)
            : this(state, name, false)
        {
            Contract.Requires(state != null);

        }

        public int Count
        {
            get
            {
                var count =  (int)ListHelperMethods.Length(m_state, new object[] { m_name });
                Contract.Assume(count >= 0, "Making the assumption explicit");
                return count;
            }
        }

        public object this[int index]
        {
            get
            {
                return ListHelperMethods.GetAt(m_state, new object[] { m_name, index });
            }
            set
            {
                ListHelperMethods.UpdateItemAt(m_state, new object[] { m_name, index, value });
            }
        }

        public bool IsFixedSize
        {
            get { return false; }
        }

        public bool IsReadOnly
        {
            get { return false; }
        }

        public bool IsSynchronized
        {
            get { return true; }
        }

        public object SyncRoot
        {
            get { return m_state; }
        }

        public int Add(object item)
        {
            var count = (int)ListHelperMethods.Push(m_state, new object[] { m_name, "right", item });
            Contract.Assume(count >= 0, "Making the assumption explicit");
            return count;
        }

        public void AddRange(IEnumerable<object> items)
        {
            Contract.Requires(items != null); // Need contract, as this method is *not* defined on one of our interfaces.

            object[] parms = new object[2 + items.Count()];
            parms[0] = m_name;
            parms[1] = "right";
            int i = 0;
            foreach (object item in items)
            {
                parms[i + 2] = item;
                i++;
            }

            ListHelperMethods.Push(m_state, parms);
        }

        public void Clear()
        {
            ListHelperMethods.Clear(m_state, new object[] { m_name });
        }

        public bool Contains(object item)
        {
            int found = (int)ListHelperMethods.IndexOf(m_state, new object[] { m_name, item });
            return found != -1;
        }

        public void CopyTo(object[] array)
        {
            Contract.Requires(array != null); // Need contract, as this method is *not* defined on one of our interfaces.

            List<object> items = (List<object>)ListHelperMethods.Enumerate(m_state, new object[] { m_name, 0, -1 });
            if (array.Length < items.Count())
                throw new ArgumentException("The number of elements in the source List is greater than the number of elements that the destination array can contain.");

            int i = 0;
            foreach (object item in items)
            {
                Contract.Assume(i < array.Length, "Let us tell cccheck that no element has been added to the items list");
              
                array[i] = item;
                i++;
            }
        }

        public void CopyTo(Array array, int index)
        {
            List<object> items = (List<object>)ListHelperMethods.Enumerate(m_state, new object[] { m_name, 0, -1 });
            if (array.Length < items.Count())
                throw new ArgumentException("The number of elements in the source List is greater than the number of elements that the destination array can contain.");

            int i = 0;
            foreach (object item in items)
            {
                array.SetValue(item, index + i);
                i++;
            }
        }

        public void CopyTo(object[] array, int index)
        {
            Contract.Requires(array != null); // Need contract, as this method is *not* defined on one of our interfaces.

            List<object> items = (List<object>)ListHelperMethods.Enumerate(m_state, new object[] { m_name, index, -1 });
            if (array.Length < items.Count())
                throw new ArgumentException("The number of elements in the source List is greater than the number of elements that the destination array can contain.");

            int i = 0;
            foreach (object item in items)
            {
                Contract.Assume(i < array.Length, "Let us tell cccheck that no element has been added to the items list");
                array[i] = item;
                i++;
            }
        }

        public void CopyTo(int index, object[] array, int arrayIndex, int count)
        {
            Contract.Requires(array != null); // Need contract, as this method is *not* defined on one of our interfaces.

            List<object> items = (List<object>)ListHelperMethods.Enumerate(m_state, new object[] { m_name, index, index + count });
            if (array.Length < items.Count())
                throw new ArgumentException("The number of elements in the source List is greater than the number of elements that the destination array can contain.");

            int i = 0;
            foreach (object item in items)
            {
                array[arrayIndex + i] = item;
                i++;
            }
        }

        public IEnumerator GetEnumerator()
        {
            return new EmbeddedListEnum(m_state, m_name);
        }

        List<object> GetRange(int index, int count)
        {
            return (List<object>)ListHelperMethods.Enumerate(m_state, new object[] { m_name, index, index + count });
        }

        public int IndexOf(object item)
        {
            var index = (int)ListHelperMethods.IndexOf(m_state, new object[] { m_name, item });
            Contract.Assume(index >= -1, "Making the assumption explicit");
            return index;
        }

        public int IndexOf(object item, int index)
        {
            int found = (int)ListHelperMethods.IndexOf(m_state, new object[] { m_name, item });
            if (found != -1 && found >= index)
            {
                return found - index;
            }
            else
            {
                return -1;
            }
        }

        public int IndexOf(object item, int index, int count)
        {
            int found = (int)ListHelperMethods.IndexOf(m_state, new object[] { m_name, item });
            if (found != -1 && found >= index && found < index + count)
            {
                return found - index;
            }
            else
            {
                return -1;
            }
        }

        public void Insert(int index, object item)
        {
            ListHelperMethods.InsertAt(m_state, new object[] { m_name, index, item });
        }

        public void Remove(object item)
        {
            ListHelperMethods.Remove(m_state, new object[] { m_name, 1, item });
        }

        public void RemoveAt(int index)
        {
            ListHelperMethods.RemoveAt(m_state, new object[] { m_name, index });
        }

        public void RemoveRange(int index, int count)
        {
            ListHelperMethods.RemoveRange(m_state, new object[] { m_name, index, count });
        }

        public void Sort()
        {
            ListHelperMethods.Sort(m_state, new object[] { m_name });
        }

        // explicit  dispose call - destroy list
        public void Dispose()
        {
            if (!m_disposed)
            {
                // If list is not there, this does nothing
                ListHelperMethods.Destroy(m_state, new object[] { m_name });
                m_disposed = true;
            }
        }

    }

    // Enumerator for ListActor
    // List should not be modified while using enumerator
    struct EmbeddedListEnum : IEnumerator
    {
        private readonly IActorState m_state;
        private readonly string m_name;
        private string m_cursor;
        private object m_current;

        public EmbeddedListEnum(IActorState state, string name)
        {
            m_state = state;
            m_name = name;
            m_current = null;
            m_cursor = null;
            Reset();
        }

        public object Current
        {
            get
            {
                return m_current;
            }
        }

        public bool MoveNext()
        {
            if (m_cursor == null)
                return false;

            Tuple<object, string> next = ListHelperMethods.GetNext(m_state, new object[] { m_name, m_cursor }) as Tuple<object, string>;
            if (next == null)
            {
                m_cursor = null;
                m_current = null;
                return false;
            }
            else
            {
                m_current = next.Item1;
                m_cursor = next.Item2;
                return true;
            }
        }

        public void Reset()
        {
            m_cursor = (string)ListHelperMethods.GetFirst(m_state, new object[] { m_name });
        }
    }
}
